using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

namespace VGS {
    public class Lineup : IEnumerable {
        // [0] = position 1 on the court, [5] = position 6.
        private Player[] lineup = new Player[6]; // 6 players in a lineup

        public EventHandler<LineupEventArgs> PlayerAdded;
        public EventHandler<LineupEventArgs> PlayerRemoved;
        public EventHandler PlayersRotated;


        #region Properties
        /// <summary>
        /// Checks whether or not the lineup is full
        /// </summary>
        /// <returns>true if the lineup is full, otherwise false</returns>
        public bool IsFull
        {
            get
            {
                for (int i = 0; i < 6; i++)
                {
                    if (lineup[i] == null)
                        return false;
                }
                return true;
            }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor. Resets all player positions to null, which also
        /// represents an empty, non-filled position.
        /// </summary>
        public Lineup()
        {
            for (int i = 0; i < 6; i++)
                lineup[i] = null;
        }
        #endregion

        #region Methods
        #region OnEvent methods
        protected virtual void OnPlayerAdded(LineupEventArgs e)
        {
            if (PlayerAdded != null) PlayerAdded(this, e);
        }

        protected virtual void OnPlayerRemoved(LineupEventArgs e)
        {
            if (PlayerRemoved != null) PlayerRemoved(this, e);
        }

        protected virtual void OnPlayersRotated(EventArgs e)
        {
            if (PlayersRotated != null) PlayersRotated(this, e);
        }
        #endregion

        /// <summary>
        /// Gets the player at position pos
        /// </summary>
        /// <param name="pos">The volleyball-based index in the lineup (1-6)</param>
        /// <exception cref="ArgumentOutOfRangeException">index must be between 1-6</exception>
        /// <returns>The player on the specified volleyball-based position</returns>
        public Player this [int pos]
        {
            get
            {
                if (pos < 1 || pos > 6)
                    throw new ArgumentOutOfRangeException("pos must be between 1-6");
                return lineup[pos - 1];
            }
        }

        /// <summary>
        /// Returns the volleyball-based position of player p. Before this method is
        /// called, make sure to check that the player actually is in the lineup.
        /// </summary>
        /// <seealso cref="Exists(Player p)"/>
        /// <param name="p">The player</param>
        /// <exception cref="InvalidOperationException">Player not in lineup</exception>
        /// <returns>The volleyball-based position of player p</returns>
        public int PositionOfPlayer(Player p)
        {
            for (int i = 0; i < 6; i++)
            {
                // Check if they reference the same memory location
                if (lineup[i] == p)
                    return i + 1;
            }
            throw new InvalidOperationException("Player not in lineup");
        }

        /// <summary>
        /// Resets the lineup
        /// </summary>
        public void Clear()
        {
            for (int i = 1; i < 7; i++)
                Remove(i);
        }

        /// <summary>
        /// Adds a player to the lineup
        /// </summary>
        /// <param name="player">The player</param>
        /// <param name="pos">The player's volleyball position in the lineup (1-6)</param>
        /// <exception cref="ArgumentOutOfRangeException">pos must be between 1-6</exception>
        public void Add(Player player, int pos)
        {
            if (pos < 1 || pos > 6 || player == null)
                throw new ArgumentOutOfRangeException("pos must be between 1-6");

            // Add the player to the lineup
            lineup[pos - 1] = player;
            // Call the event
            OnPlayerAdded(new LineupEventArgs(pos, player));
        }


        /// <summary>
        /// Removes a player from the lineup. (Sets its entry to null.)
        /// </summary>
        /// <param name="pos">The player's position, 1-6</param>
        /// <exception cref="ArgumentOutOfRangeException">pos must be between 1-6</exception>
        public void Remove(int pos) {
            if (pos < 1 || pos > 6)
                throw new ArgumentOutOfRangeException("pos must be between 1-6");

            if (lineup[pos - 1] != null)
            {
                Player p = lineup[pos - 1];
                lineup[pos - 1] = null; // "Remove the player"
                // call the event
                OnPlayerRemoved(new LineupEventArgs(pos, p));
            }
        }

        /// <summary>
        /// Removes a player from the lineup. (Just sets its entry to null.)
        /// </summary>
        /// <param name="shirt">The players shirt</param>
        public void Remove(string shirt) {
            for (int i = 0; i < 6; i++) {
                if (lineup[i].Shirt == shirt) {
                    Player p = lineup[i];
                    lineup[i] = null;
                    // call the event
                    OnPlayerRemoved(new LineupEventArgs(i+1, p));
                }                
            }
        }


        /// <summary>
        /// This method rotates the player one step. The rotation is based on
        /// the volleyball position, and when iterating through the lineup,
        /// one should be able to add each player to court, according to the
        /// volleyball position (look below)
        /// 
        /// |=============|
        /// |  4   3   2  |
        /// |             |
        /// |  5   6   1  |
        /// |_____________|
        /// 
        /// After the rotation player 1 will be at pos 6, player 2 at pos 1 and so forth
        /// 
        /// pos '1' is lineup[0], pos '2' is lineup[1] and so on
        /// </summary>
        public void Rotate() {
            Player tmp1, tmp2;

            tmp1 = lineup[5];
            lineup[5] = lineup[0];

            tmp2 = lineup[4];
            lineup[4] = tmp1;

            tmp1 = lineup[3];
            lineup[3] = tmp2;

            tmp2 = lineup[2];
            lineup[2] = tmp1;

            tmp1 = lineup[1];
            lineup[1] = tmp2;

            //tmp2 = lineup[5];
            lineup[0] = tmp1;

            // Notify that the lineup has rotated
            OnPlayersRotated(EventArgs.Empty);
        }


        /// <summary>
        /// Checks if player p is on the lineup
        /// </summary>
        /// <param name="p">The player</param>
        /// <returns>true if p is on the lineup, else false</returns>
        public bool Exists(Player p)
        {
            for (int i = 0; i < 6; i++)
            {
                // Check if they reference the same memory location
                if (lineup[i] == p)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Checks if a player on position pos is in the lineup
        /// </summary>
        /// <param name="pos">The volleyball-based position (1-6)</param>
        /// <returns>true if a player exists at that position, else false</returns>
        public bool Exists(int pos)
        {
            if (lineup[pos - 1] != null)
                return true;
            return false;
        }
        #endregion


        /// <summary>
        /// Returns enumerator
        /// </summary>
        /// <returns>IEnumerator</returns>
        public IEnumerator GetEnumerator() {
            return new LineupEnum(lineup);
        }
    }
}
